﻿using EntityFramework.Data.Core;
using Framework.Common;
using Framework.Common.Paged;
using IRepositories.Core;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using YouTravel.DomainModels;
using YouTravel.Framework.Enums;
using YouTravel.QueryModels;
using YouTravel.ViewModels;
using System.Data.Entity;
using Framework.Common.Exceptions;
namespace YouTravel.DomainService
{
    public class InvoiceService : IInvoiceService
    {
        readonly IEFRepository<Invoice> _invoiceRepository;
        readonly IEFRepository<InvoiceSubject> _invoiceSubjectRepository;
        readonly IEFRepository<Contract> _contractRepository;
        readonly IEFRepository<Special> _specialRepository;
        public InvoiceService(IUnitOfWork db, IEFRepository<Invoice> invoiceRepository,
            IEFRepository<InvoiceSubject> invoiceSubjectRepository,
            IEFRepository<Contract> contractRepository,
            IEFRepository<Special> specialRepository)
        {
            this._invoiceRepository = invoiceRepository;
            this._invoiceSubjectRepository = invoiceSubjectRepository;
            this._contractRepository = contractRepository;
            this._specialRepository = specialRepository;
            this._contractRepository.SetDbContext(db);
            this._invoiceRepository.SetDbContext(db);
            this._invoiceSubjectRepository.SetDbContext(db);
        }
        /// <summary>
        /// 申请发票
        /// </summary>
        /// <param name="invoice"></param>
        /// <param name="operateUserId"></param>
        public void ApplyInvoice(InvoiceViewModel invoice, int operateUserId)
        {
            var loginUser = RedisHelp.GetLoginUserCacheNotNull(operateUserId);
            #region 安全验证
            if (loginUser.Type == UserType.Company)
            {
                throw new DataOperationPermissions("无此操作权限,总公司账号不能操作发票");
            }
            else if (loginUser.Type == UserType.Branch)
            {
                throw new DataOperationPermissions("无此操作权限,分社账号不能申请发票");
            }
            else if (loginUser.Type == UserType.SpecialLine)
            {
                if (loginUser.SpecialId.HasValue && loginUser.SpecialId != invoice.SpecialId)
                {
                    throw new DataOperationPermissions("无此操作权限,专线只能申请自己的发票");
                }
            }
            #endregion
            #region 数据验证
            if (invoice.Money <= 0)
                throw new DataValidationException("发票金额不能小于等于0");
            if (string.IsNullOrEmpty(invoice.Content))
                throw new DataValidationException("发票详细不能为空");
            if (!invoice.Content.ValidateLen(500))
                throw new DataValidationException("发票详细填写过多,请分批申请");
            if (invoice.ParValueTime < DateTime.Parse("1753-01-01"))
                throw new DataValidationException("请填写正确的票面日期");
            #endregion
            #region 欠款判断

            var special = _specialRepository.GetModel().Include(p => p.SpecialCapital)
                .Include(p => p.Branch)
                .SingleOrDefault(p => p.ID == invoice.SpecialId);
            if (special == null)
                throw new BusinessException("专线不存在");
            if (special.Status == SpecialStatus.Freeze)
            {
                throw new BusinessException("所属专线已被冻结");
            }
            if (special.Status == SpecialStatus.AccountCancellation)
            {
                throw new BusinessException("所属专线已被销户");
            }
            //专线已开票+当前开票+当前专线所有待审核金额总数-专线已收款>=专线欠款最大额度
            //得到当前专线所有待审核金额总数
            var sumMoney = _invoiceRepository.GetModel(p => p.SpecialId == special.ID && p.Status == InvoiceStatus.WaitInvoice).Sum(s => (decimal?)s.Money) ?? 0;
            if (special.SpecialCapital.InvoiceSend + invoice.Money + sumMoney - special.SpecialCapital.Gathering >= special.Branch.ArrearsTotal)
            {
                throw new BusinessException("你提交发票金额已超出设置的最大欠款额度,暂时不能提交发票申请");
            }
            #endregion

            Invoice model = new Invoice();

            model.Status = InvoiceStatus.WaitInvoice;
            model.SpecialId = invoice.SpecialId;
            model.SpecialName = special.Name;

            model.SellerId = invoice.SellerId;
            model.Title = invoice.SellerSupplier.Name;
            model.ParValueTime = invoice.ParValueTime;
            model.Money = invoice.Money;
            model.Content = invoice.Content;
            model.ApplicantId = loginUser.ID;
            model.ApplicantName = loginUser.Name;
            model.ApplicationTime = DateTime.Now;


            _invoiceRepository.Insert(model);
        }
        /// <summary>
        /// 审核发票
        /// 
        /// 增加已开发票金额
        /// 
        /// 注:
        /// 只有分社与超级管理员用户 能使用此接口
        /// </summary>
        /// <param name="invoice"></param>
        /// <param name="operateUserId"></param>
        public void AuditInvoices(InvoiceViewModel invoice, int operateUserId)
        {
            var loginUser = RedisHelp.GetLoginUserCacheNotNull(operateUserId);
            #region 安全验证
            switch (loginUser.Type)
            {
                case UserType.Company:
                    throw new DataOperationPermissions("无此操作权限,总公司账号不能操作发票");
                case UserType.SpecialLine:
                    throw new DataOperationPermissions("无此操作权限,专线账号不能审核发票");
            }

            #endregion
            if (!string.IsNullOrEmpty(invoice.AuditRemark) && invoice.AuditRemark.Length > 200)
                throw new DataValidationException("备注不能超过200个汉字");
            if (invoice.Status == InvoiceStatus.OpenInvoice)
            {
                if (invoice.Express == null)
                    throw new BusinessException("快递信息不能为空");
                if (!invoice.Express.Number.ValidateLen(50))
                    throw new DataValidationException("快递单号长度必须在1-50字符之间");
                if (!invoice.Express.Name.ValidateLen(50))
                    throw new DataValidationException("快递名称长度必须在1-50字符之间");
                if (invoice.Express.Money <= 0)
                    throw new DataValidationException("快递费用必须大于0");
            }
            else
            {
                if (invoice.Status == InvoiceStatus.DiscardInvoice && string.IsNullOrEmpty(invoice.AuditRemark))
                    throw new DataValidationException("当发票审核为废弃时,备注不能为空");
            }
            if (invoice.Status == InvoiceStatus.WaitInvoice)
                throw new DataValidationException("参数传入错误,当前接口发票状态参数不能为待开具");

            //得到 待开具状态发票
            var model = _invoiceRepository.GetModelTracking()
                .Include(p => p.Express)
                .Include(p => p.Special.SpecialCapital)
                .Include(p => p.Special.Branch)
                .SingleOrDefault(p => p.Status == InvoiceStatus.WaitInvoice && p.ID == invoice.ID);

            if (model == null)
                throw new BusinessException("此发票不存在");
            if (model.Special.SpecialCapital == null)
                throw new BusinessException("此发票所属专线缺少资金信息");
            if (loginUser.Type == UserType.Branch)
            {
                if (model.Special.BranchId != loginUser.BranchId)
                    throw new DataOperationPermissions("无此操作权限,分社账号只能操作自身下属专线发票申请");
            }
            //当状态为废弃时则没有快递信息
            if (invoice.Status == InvoiceStatus.OpenInvoice)
                model.Express = invoice.Express.MapNew<ExpressViewMdoel, Express>();

            model.Status = invoice.Status;
            model.AuditorId = loginUser.ID;
            model.AuditorName = loginUser.Name;
            model.AuditRemark = invoice.AuditRemark;
            model.AuditTime = DateTime.Now;
            #region 增加已开发票金额

            if (model.Status == InvoiceStatus.OpenInvoice)
            {
                //专线已开票+当前开票-专线已收款>=专线欠款最大额度
                if (model.Special.SpecialCapital.InvoiceSend + model.Money - model.Special.SpecialCapital.Gathering >=
                    model.Special.Branch.ArrearsTotal)
                {
                    throw new BusinessException("已超出设置的最大欠款额度,暂时不能审核通过");
                }

                model.Special.SpecialCapital.InvoiceSend = model.Special.SpecialCapital.InvoiceSend + model.Money;
            }

            #endregion

            _invoiceRepository.Update(model);
        }
        /// <summary>
        /// 得到指定专线发票科目正常状态的数据
        /// 注:
        /// 只有专线与超级管理员用户 能使用此接口 
        /// 专线 查询他所属分社的发票科目
        /// 超级管理员 查询指定 所有发票科目
        /// </summary>
        /// <param name="specialId"></param>
        /// <param name="operateUserId"></param>
        public List<ComboxData> GetInvoiceSubject(int specialId, int operateUserId)
        {
            //得到专线
            var special = _specialRepository.GetModel().SingleOrDefault(p => p.ID == specialId);
            if (special == null)
                throw new BusinessException("专线不存在");
            if (special.Status == SpecialStatus.Freeze)
            {
                throw new BusinessException("所选专线已被冻结");
            }
            if (special.Status == SpecialStatus.AccountCancellation)
            {
                throw new BusinessException("所选专线已被销户");
            }

            var loginUser = RedisHelp.GetLoginUserCacheNotNull(operateUserId);
            #region 安全验证
            if (loginUser.Type == UserType.Company)
            {
                throw new DataOperationPermissions("无此访问权限,总公司账号不能查看发票");
            }//分社 查看 自身所有发票科目
            else if (loginUser.Type == UserType.Branch)
            {
                throw new DataOperationPermissions("无此访问权限,分社账号不能访问此接口");
            }//专线 查看 他所属分社的所有发票科目
            else if (loginUser.Type == UserType.SpecialLine)
            {
                if (loginUser.BranchId == special.BranchId)
                {
                    throw new DataOperationPermissions("无此访问权限,专线账号只能查看他所属分社的所有发票科目");
                }
            }
            #endregion

            //发票科目状态正常 
            //所属分社状态正常
            var listCombox = _invoiceSubjectRepository.GetModel()
                .Include(p => p.Branch)
                .Where(p => p.Status
                    && !p.Type
                    && p.BranchId == special.BranchId && p.Branch.Status)
                .Select(s => new ComboxData()
                {
                    Key = s.ID,
                    Value = s.InvoiceSubjectName,
                    ExtValue = new
                    {
                        s.BranchId
                    }
                }).ToList();
            return listCombox;

        }
        /// <summary>
        /// 得到指定专线销售商/供应商 正常状态的数据
        /// 注:
        /// 只有专线与超级管理员用户 能使用此接口
        /// 专线 查询他下级所有销售商/供应商
        /// 超级管理员 查询所有销售商/供应商
        /// </summary>
        /// <param name="operateUserId"></param>
        /// <returns></returns>
        public List<ComboxData> GetSellerSupplier(int operateUserId)
        {
            var loginUser = RedisHelp.GetLoginUserCacheNotNull(operateUserId);
            #region 安全验证
            if (loginUser.Type == UserType.Company)
            {
                throw new DataOperationPermissions("无此访问权限,总公司账号不能查看发票");
            }//分社 查看 自身所有发票科目
            else if (loginUser.Type == UserType.Branch)
            {
                throw new DataOperationPermissions("无此访问权限,分社账号只能查看自身所有发票科目");
            }
            #endregion
            //注:
            //   专线销售商/供应状态正常 
            //   合同状态正常  
            //   专线状态正常 
            //   分社状态正常
            var query = _contractRepository.GetModel()
                .Include(p => p.Special.Branch)
                .Include(p => p.SellerSupplier)
                .Where(p => p.SpecialId != null && p.Status == ContractStatus.Normal
                    && p.SellerSupplierId != null && p.SellerSupplier.Status && p.SellerSupplier.Type == SellerSupplierType.Seller
                    && p.Special.Status == SpecialStatus.Normal
                    && p.Special.Branch.Status);
            //登录人为专线时 查询自己专线下所有
            if (loginUser.Type == UserType.SpecialLine)
            {
                query = query.Where(p => p.SpecialId == loginUser.SpecialId);
            }

            var listCombox = query.Select(s => new ComboxData
            {
                Key = s.SellerSupplierId.Value,
                Value = s.SellerSupplier.Name,
                ExtValue = new
                {
                    s.SpecialId
                }
            }).ToList();
            return listCombox;
        }

        /// <summary>
        /// 得到发票详细
        /// </summary>
        /// <param name="invoiceId"></param>
        /// <param name="operateUserId"></param>
        /// <returns></returns>
        public InvoiceViewModel GetModel(int invoiceId, int operateUserId)
        {
            //得到查询
            var query = _invoiceRepository.GetModel()
                .Include(p => p.Express)
                .Include(p => p.Special)
                .Where(p => p.ID == invoiceId);

            var model = query.SingleOrDefault();
            if (model == null)
            {
                throw new BusinessException("此发票不存在");
            }

            BuidDataAuthorityVerify(operateUserId, model);

            return model.MapNew<Invoice, InvoiceViewModel>("Special", "SellerSupplier", "Applicant", "Auditor");
        }
        /// <summary>
        /// 得到发票列表
        /// </summary>
        /// <param name="queryCond"></param>
        /// <returns></returns>
        public CustomPagedList<InvoiceViewModel> GetList(PagedParam<InvoiceQuery> queryCond)
        {
            //得到分页数据
            var result = _invoiceRepository.GetModel().GetPagedList(queryCond.PageDto.PageNo, queryCond.PageDto.PageSize,
                BuildQueryCondition(queryCond.QueryDto), queryCond.SortField)
                .ToCustomPagedList(u => u.MapNew<Invoice, InvoiceViewModel>("Special", "SellerSupplier", "Applicant", "Auditor"));
            return result;
        }
        #region private
        /// <summary>
        /// 绑定查询条件
        /// 注:
        /// 分社      :    查询时条件不做限制 可查询专线所有状态信息
        /// 专线      :    当专线被冻结或销户后 是不能登录不能查询所有，所以下面就在做验证
        /// </summary>
        /// <param name="userQuery"></param>
        /// <returns></returns>
        private Func<IQueryable<Invoice>, IQueryable<Invoice>> BuildQueryCondition(InvoiceQuery userQuery)
        {
            return query =>
            {
                if (userQuery != null)
                {
                    //联查对象
                    query = query.Include(p => p.Special).Include(p => p.Express);
                    //数据权限
                    query = BuidQueryDataAuthority(userQuery.LoginUserId)(query);
                    #region 权限范围

                    var loginUser = RedisHelp.GetLoginUserCacheNotNull(userQuery.LoginUserId);
                    if (loginUser.Type == UserType.Company)
                    {
                        throw new DataOperationPermissions("无此操作权限,总公司账号不能查看发票");
                    }//分社 查看 他下属所有专线的发票申请
                    if (loginUser.Type == UserType.Branch)
                    {
                        query = query.Where(p => p.Special.BranchId == loginUser.BranchId);
                    }//专线 查看 他自己的发票申请 并且专线正常 分社正常
                    else if (loginUser.Type == UserType.SpecialLine)
                    {
                        query = query.Where(p => p.Special.ID == loginUser.SpecialId
                            && p.Special.Status == SpecialStatus.Normal
                            && p.Special.Branch.Status);
                    }

                    #endregion



                    //专线名称或负责人姓名
                    if (!string.IsNullOrEmpty(userQuery.Name))
                    {
                        query = query.Where(p => p.Special.Name.Contains(userQuery.Name) || p.Special.ChargeName.Contains(userQuery.Name));
                    }
                    if (userQuery.StartData.HasValue)
                    {
                        var start = userQuery.StartData.Value.Date;
                        query = query.Where(p => p.ApplicationTime >= start);
                    }
                    if (userQuery.EndData.HasValue)
                    {
                        var end = userQuery.EndData.Value.AddDays(1).Date;
                        query = query.Where(p => p.ApplicationTime < end);
                    }
                    if (userQuery.Status.HasValue)
                    {
                        query = query.Where(p => p.Status == userQuery.Status.Value);
                    }
                }
                return query;
            };
        }
        /// <summary>
        /// 判断此服务中方法中的数据权限
        /// 总公司     :   无权限
        /// 分社       :   查看列表(自己下属数据)、审核发票(自己下属数据)、得到发票详细(只能查看自己下属数据)
        /// 专线       :   申请发票(自己属在专线)、查看列表(自己所在专线)
        /// 超级管理员 :   所有权限(所有数据)
        /// </summary>
        /// <param name="operateUserId"></param>
        /// <param name="customValidation"></param>
        /// <returns></returns>
        private Func<IQueryable<Invoice>, IQueryable<Invoice>> BuidQueryDataAuthority(int operateUserId, Action<UserViewModel> customValidation = null)
        {
            return query =>
            {
                var loginUser = RedisHelp.GetLoginUserCacheNotNull(operateUserId);
                if (customValidation != null)
                    customValidation(loginUser);
                if (loginUser.Type == UserType.Company)
                {
                    throw new DataOperationPermissions("无此操作权限,总公司账号不能查看发票");
                }//分社 查看 他下属所有专线的发票申请
                else if (loginUser.Type == UserType.Branch)
                {
                    query = query.Where(p => p.Special.BranchId == loginUser.BranchId);
                }//专线 查看 他自己的发票申请
                else if (loginUser.Type == UserType.SpecialLine)
                {
                    query = query.Where(p => p.Special.ID == loginUser.SpecialId);
                }

                return query;
            };
        }
        /// <summary>
        /// 判断此服务中方法中的数据权限
        /// 总公司     :   无权限
        /// 分社       :   查看列表(自己下属数据)、审核发票(自己下属数据)、得到发票详细(只能查看自己下属数据)
        /// 专线       :   申请发票(自己属在专线)、查看列表(自己所在专线)
        /// 超级管理员 :   所有权限(所有数据)
        /// </summary>
        /// <param name="operateUserId"></param>
        /// <param name="invoice"></param>
        private void BuidDataAuthorityVerify(int operateUserId, Invoice invoice)
        {
            var loginUser = RedisHelp.GetLoginUserCacheNotNull(operateUserId);

            if (loginUser.Type == UserType.Company)
            {
                throw new DataOperationPermissions("无此访问权限,总公司账号不能查看发票");
            }//分社 查看 他下属所有专线的发票申请
            else if (loginUser.Type == UserType.Branch)
            {
                if (loginUser.BranchId != invoice.Special.BranchId)
                {
                    throw new DataOperationPermissions("无此访问权限,分社账号只能查看他下属所有专线发票申请");
                }
            }//专线 查看 他自己的发票申请
            else if (loginUser.Type == UserType.SpecialLine)
            {
                if (loginUser.ID == invoice.SpecialId)
                {
                    throw new DataOperationPermissions("无此访问权限,专线账号只能查看自身所在专线发票申请");
                }
            }
        }
        #endregion

    }
}
